/*
  Copyright 2005 - Daniel Lowd

  This file is part of the nbe code available at http://www.cs.washington.edu/ai/nbe/. There are some modifications to 
  integrate the original code of Daniel Lowd into the eapmlib library. The NBE method is explained in:

  Daniel Lowd and Pedro Domingos. 'Naive Bayes Models for Probability Estimation'. 
  Proceedings of the Twenty-Second International Conference on Machine Learning (ICML), 2005. 
  Bonn, Germany: ACM Press.
*/
#include "Prob.h"

using namespace NBE;
Prob NBE::Prob::slowMult(const Prob& other) const
{
    Prob ret;
    ret.vGood = false;
    ret.logvGood = false;

    if (logvGood && other.logvGood) {
        ret.logv = logv + other.logv;
        ret.logvGood = true;
    }

    if (vGood && other.vGood) {
        ret.v = v * other.v;
        if (ret.v > REALLY_SMALL_PROB) {
            ret.vGood = true;
        }
    }

    if (ret.vGood || ret.logvGood) {
        return ret;
    }

    ret.logv = ln() + other.ln();
    ret.logvGood = true;
    return ret;
}

Prob NBE::Prob::slowDiv(const Prob& other) const
{
    Prob ret;
    ret.vGood = false;
    ret.logvGood = false;

    if (logvGood && other.logvGood) {
        ret.logv = logv - other.logv;
        ret.logvGood = true;
    }

    if (vGood && other.vGood) {
        ret.v = v / other.v;
        if (ret.v > REALLY_SMALL_PROB) {
            ret.vGood = true;
        }
    }

    if (ret.vGood || ret.logvGood) {
        return ret;
    }

    ret.logv = ln() - other.ln();
    ret.logvGood = true;
    return ret;
}

Prob& NBE::Prob::operator*=(const Prob& other) 
{
    // TODO: make this more efficient...
    (*this) = (*this) * other;
    return (*this);
}

Prob& NBE::Prob::operator/=(const Prob& other) 
{
    // TODO: make this more efficient...
    (*this) = (*this) / other;
    return (*this);
}

Prob NBE::Prob::operator+(const Prob& other) const
{
    if (isReallySmall() || other.isReallySmall()) {
        if (ln() - other.ln() > 25) {
            return *this;
        } else if (other.ln() - ln() > 25) {
            return other;
        } else {
            Prob ret;
            ret.logv = log(exp(ln() - other.ln()) + 1) + other.ln();
            ret.vGood = false;
            ret.logvGood = true;
            return ret;
        }
    } else {
        return Prob(val() + other.val());
    }
}

#include <iostream>
Prob& NBE::Prob::operator+=(const Prob& other)
{
    if (isReallySmall() || other.isReallySmall()) {
        if (ln() - other.ln() > 25 || other.isZero()) {
            // Add nothing
        } else if (other.ln() - ln() > 25 || isZero()) {
            // Use the argument
            *this = other;
        } else {
            // Do sum of logs
            logv = log(exp(ln() - other.ln()) + 1) + other.ln();
            logvGood = true;
            vGood = false;
        }
    } else {
        v = val() + other.val();
        vGood = true;
        logvGood = false;
    }

    return *this;
}

Prob& NBE::Prob::operator=(const Prob& other)
{
    v = other.v;
    logv = other.logv;
    vGood = other.vGood;
    logvGood = other.logvGood;
    return (*this);
}
